home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d7
/
jmodm308.arc
/
JMODEM_A.C
< prev
next >
Wrap
Text File
|
1991-01-02
|
36KB
|
571 lines
/****************************************************************************/
/* FILE JMODEM_A.C */
/* */
/* The JMODEM protocol MicroSoft (r) 'C' V5.1 */
/* Created 03-FEB-1990 Richard B. Johnson */
/* 405 Broughton Drive */
/* Beverly, Massachusetts 01915 */
/* BBS (508) 922-3166 */
/* */
/* An external protocol for high-speed data transmission. */
/* */
/* This is the MAIN module */
/* The required modules are: */
/* JMODEM.H (function prototypes and structures) */
/* UART.H (8250 UART parameters) */
/* SCREEN.H (function protypes and structures for the screen) */
/* JMODEM_A.C (this module) */
/* JMODEM_B.C (memory allocation and input parsing) */
/* JMODEM_C.C (all file I/O) */
/* JMODEM_D.C (encode/decode and CRC routines) */
/* JMODEM_E.C (communications I/O routines) */
/* JMODEM_F.C (the screen I/O routines) */
/* JMODEM. (The MAKE file ) */
/* */
/* This program requires about 67k of free RAM to execute properly. */
/* If you have 66k or less, it will execute, but the screens will */
/* not be written or replaced properly. If you have only 64k, the */
/* program will exit with an error message. */
/* */
/* Revision History: */
/* V3.00 Beta test 11-FEB-1990 Richard B. Johnson */
/* V3.01 First release 18-FEB-1990 Richard B. Johnson */
/* V3.02 Revised 19-FEB-1990 Richard B. Johnson */
/* */
/* (1) A bug in MicroSoft _calloc() allocates overlapping */
/* buffers so data files were getting corrupted. I had */
/* used both _calloc() and _malloc() at the same time and */
/* they didn't like it. I changed the memory allocation */
/* to _malloc() only and it seems to work okay. */
/* */
/* (2) While debugging, I found some structures I didn't need and */
/* removed them. Changed some code to accommodate. */
/* */
/* (3) Added a file-size during downloads. */
/* */
/* (4) Changed code in the data encoding (compression) routine */
/* in an attempt to speed it up. */
/* */
/* V3.03 Revised 20-FEB-1990 Richard B. Johnson */
/* */
/* (5) Fixed bug in compression routine where the loop wasn't */
/* terminating properly, adding random characters. Bug was */
/* created during V3.02 change. */
/* */
/* V3.04 Revised 27-FEB-1990 Richard B. Johnson */
/* */
/* (1) Modified the block-size routine and the receive-block */
/* routine in an attempt to improve the noise immunity. */
/* Does not abort even if you whistle into the telephone */
/* during uploads and downloads. Waits 5 seconds to clear */
/* the interrupt buffer when a bad block-size is received. */
/* */
/* (2) Added a 1/2 second wait for modem status when opening */
/* channel. This might accommodate slow modems response to */
/* RTS. */
/* */
/* V3.05 Revised 22-MAR-1990 Richard B. Johnson */
/* */
/* (1) Removed _sprintf() runtime library calls to shorten */
/* the code. Saved about 4k. */
/* */
/* (2) Removed extra spaces in the signon-logo to shorten */
/* the program size. */
/* */
/* (3) Changed the method of creating a fixed-length string */
/* for both the block size and cps numbers which saved about */
/* 800 bytes of program size. */
/* */
/* (4) Changed numerous array indexes in JMODEM_F.C to pointers */
/* to reduce code size. Saved a few hundred bytes and should */
/* improve speed of screen output. */
/* */
/* (5) Created a local _puts() routine which saved over 6k from the */
/* MicroSoft C runtime library version. (JMODEM_F.C) */
/* */
/* V3.06 Revised 07-APR-1990 Richard B. Johnson */
/* */
/* (1) Put the filename text into the syst structure as a pointer */
/* to char. This allowed me to save 56 bytes of code and now */
/* only two parameters are passed to the _screen() function. */
/* */
/* (2) Modified the syst structure and supporting code. */
/* */
/* (3) Moved all external data and functions to the JMODEM.H file. */
/* */
/* (4) Moved _disp() "usage" module to JMODEM_F.C */
/* */
/* (5) Changed arrays in JMODEM_B.C to pointers to reduce code- */
/* size. Eliminated _strcpy() from the command-line parsing */
/* routines. Brought the code-size to less than 12,000 bytes. */
/* */
/* (6) Reduced the code-size in the _encode(), _decode(), and */
/* _crc() routines in JMODEM_D.C. Removed shifts to improve */
/* speed and replaced the shifts with pointers for altering */
/* portions of the strings. */
/* */
/* (7) Made a _cancel() routine in JMODEM_A.C to send ^Xes upon */
/* abort. */
/* */
/* (8) Removed the bit being set "OUT 1" via the modem-control */
/* register in the "open" routine in JMODEM_E.C. This was */
/* causing some internal modems to lock up as they use this */
/* bit for something. "OUT 2" is used to enable IRQ on most */
/* clone RS-232 boards and modems. The Heathkit HZ-100 boards */
/* will probably not work anymore because they use "OUT 1". */
/* */
/* */
/* V3.07 Revised 03-MAY-1990 Richard B. Johnson */
/* */
/* (1) Rewrote code to remove the requirement for a file buffer. */
/* This means that this buffer does not need to be allocated, */
/* saving about 8k of RAM at run-time. ( JMODEM_A.C ) */
/* */
/* Program now only requires 52k of free RAM to execute okay. */
/* */
/* (2) Changed the header file, JMODEM.H, and function calling */
/* procedures to file_io() and screen() to allow variable- */
/* length parameter-lists. This eliminates the requirement */
/* to pass a NULL as a place-holder on procedures that don't */
/* always require all possible parameters to be passed. This */
/* saved about 50 bytes of code. */
/* */
/* (3) Changed the "Usage" prompt and code to reduce program size. */
/* Saved about 60 bytes. */
/* */
/* (4) Changed keyboard break interrupt in JMODEM_E.C so it sets */
/* the global timer to zero as well as setting the abort flag. */
/* */
/* V3.08 Revised 01-DEC-1991 Richard B. Johnson */
/* */
/* (1) Changed the code to compile without warning errors when */
/* using Microsoft Version 6.0. They saw fit to change the */
/* ANSI standards for declaring objects passed to functions. */
/* The new "standards" were called to my attention by */
/* Jeff Jevnisek who provided modified source. */
/* */
/* (2) Changed the method of determining a memory allocation */
/* failure. The code used to check for a NULL pointer returned */
/* from _malloc() if memory was not available. Microsoft does */
/* not allow NULL to be used for that anymore! Instead I have */
/* to either use a cast or check for (!ptr). I chose the latter. */
/* */
/* */
/****************************************************************************/
#include <stdlib.h> /* Used for _free() */
#include <stdio.h> /* Used for NULL value */
#include <string.h> /* Used for _memcpy() */
#include <time.h> /* Used for absolute time */
#include "jmodem.h" /* JMODEM primatives */
/****************************************************************************/
/* Global pointers and allocation */
/****************************************************************************/
word user_abort = 0; /* Global user abort flag */
byte *int_buffer; /* Pointer to interrupt buffer */
SYS syst; /* Structure for JMODEM status */
/****************************************************************************/
/* C O D E */
/****************************************************************************/
short main (short argc, char *argv[])
{
byte *in_buffer; /* Pointer to input buffer */
byte *out_buffer; /* Pointer to output buffer */
byte *comp_buffer; /* Pointer to compression buffer */
register byte *io_ptr; /* Select buffers to use for I/O */
register JBUF *buff; /* A pointer for the JMODEM block */
byte *file_name; /* Filename */
byte function; /* Receive, Transmit */
time_t start; /* Start time */
time_t finish; /* End time */
#ifdef FTIME /* Floating point timer */
double dat_tmp; /* Temporary variable for time */
#endif
word status=0; /* TX and RX status */
word tries; /* Attempts to send a file */
word cmp_size; /* Size after compression */
word data_written; /* Data written to the file */
word data_read; /* Data read from the file */
short handle; /* For file I/O */
if (!(file_name = get_inp (argc, argv))) /* Get file name */
{
disp(); /* Display usage message */
return JM_FNF;
}
if (!(function = get_fun (argc, argv))) /* Get function 'R' or 'S' */
{
disp(); /* Display usage message */
return JM_CMD;
}
if (!(port = get_port (argc, argv))) /* Get port '1 to 4 ' */
{
disp(); /* Display usage message */
return JM_CMD;
}
/****************************************************************************/
/* Allocate buffers */
/****************************************************************************/
in_buffer = allocate_memory(DAT_LEN); /* Get some memory for input */
if (!in_buffer)
return JM_MEM; /* No memory available */
out_buffer = allocate_memory(DAT_LEN); /* Get some memory for output */
if (!out_buffer)
return JM_MEM; /* No memory available */
comp_buffer=allocate_memory(DAT_LEN); /* Get memory for compression */
if (!comp_buffer)
return JM_MEM; /* No memory available */
int_buffer =allocate_memory(DAT_LEN); /* Memory for interrupt buffer */
if (!int_buffer)
return JM_MEM; /* No memory available */
/****************************************************************************/
screen (SCR_SGN); /* Write signon screen */
syst.s_len = BLK_SIZ; /* Set beginning block size */
syst.s_byt = 0; /* Set bytes handled */
syst.s_blk = 0; /* Starting block */
syst.s_sta = okay; /* Starting status */
switch(function) /* Functions are TX and RX */
{
/****************************************************************************/
/* Receive JMODEM file */
/****************************************************************************/
case 'R':
{
if (!file_io(CREATE, &handle, file_name) )
{
buff = (JBUF *) in_buffer; /* Assign type JBUF */
open_chan(port); /* Open com channel */
screen (SCR_STA); /* Write status block */
status = rx_sync(); /* Synchronize */
if (!status)
screen (SCR_SYR);
data_written = 0xFFFF;
tries = 10; /* Attempts to receive */
while ( (data_written) /* Write file okay */
&& (!user_abort ) /* No break key */
&& (!status ) /* Recev block okay */
&& (tries--) ) /* 10 retries */
{
time(&start); /* Get starting time */
screen (SCR_SYS,&syst); /* Show status block */
status = recv_blk ( /* Receive data-block */
&syst.s_len, /* Block length */
in_buffer); /* Input buffer */
if (status) /* If bad */
break; /* Abort the WHILE */
if( (!(calc_crc( GET_CRC, /* Calculate CRC */
syst.s_len, /* Amount to check */
in_buffer) )) /* Receiver buffer */
&& ( buff->blk_num == /* Check block also */
(byte)
(syst.s_blk +1))) /* Block number */
{
syst.s_sta = okay; /* Text pointer */
tries=10; /* Reset count */
syst.s_len -= OVRHD; /* Subtract overhead */
*out_buffer = ACK; /* Good */
write_chan(1,out_buffer); /* Send the ACK */
io_ptr = &buff->blk_dat; /* Assume normal data */
if ( (buff->blk_typ & COMP) == COMP)
{ /* If data compressed */
syst.s_len = decode ( /* Decode the data */
syst.s_len, /* Data-block length */
&buff->blk_dat, /* Where to start */
comp_buffer); /* Where to put data */
io_ptr = comp_buffer; /* Point to data */
}
data_written = file_io(WRITE, /* Write to file */
&handle, /* File handle */
io_ptr , /* Where data is */
syst.s_len); /* Amount to write */
syst.s_byt += data_written; /* Total bytes */
syst.s_blk++; /* Block number */
time(&finish); /* Get end time */
if (finish - start) /* Check div/0 */
{
#ifdef FTIME
dat_tmp = (double) data_written;
syst.s_cps = (short) (dat_tmp /
difftime(finish,start));
#else
syst.s_cps = (short) /* Calc Block CPS */
(data_written / (finish - start) );
#endif
}
/* Check end-of-file */
if ( (buff->blk_typ & EOF_) == EOF_)
{
file_io(CLOSE,&handle); /* Close file */
close_chan(port); /* Close the port */
status = JM_NRM; /* Set status */
goto cleanup; /* exit routine */
}
}
else
{
*out_buffer = NAK; /* Bad block */
syst.s_sta = retry; /* Char pointer */
write_chan(1,out_buffer); /* Send the NAK */
}
}
close_chan(port); /* Aborted */
file_io( DELETE, &handle, file_name); /* Delete bad file */
status = JM_ABT;
break; /* Exit if() {} */
}
else /* Can't create file */
{
status = JM_CRE;
break; /* Exit while() {} */
}
}
/****************************************************************************/
/* Send JMODEM file */
/****************************************************************************/
case 'S': /* Send JMODEM file */
{
if (!file_io(OPEN_READ, &handle, file_name) )
{
buff = (JBUF *)out_buffer; /* Assign type JBUF */
syst.s_byt = 0; /* Restore byte count */
open_chan(port); /* Open COM port */
screen (SCR_STA); /* Write status block */
status = tx_sync(); /* Synchronize */
if (!status)
screen (SCR_SYT);
while ( (!user_abort) /* Ctrl - break */
&& (!status) ) /* sent okay */
{
time(&start); /* Get starting time */
data_read = file_io( READ, /* Read a record */
&handle, /* File pointer */
&buff->blk_dat, /* Where to put data */
syst.s_len ); /* Amount to read */
if (!data_read) /* Past end of file */
break;
syst.s_byt += (long) data_read; /* Running count */
screen (SCR_SYS,&syst); /* Show status block */
buff->blk_num = (byte)
++syst.s_blk; /* Block number */
buff->blk_typ = NORM; /* Assume Normal */
buff->len = (data_read+OVRHD); /* Length of block */
if (data_read != syst.s_len) /* Less than request */
buff->blk_typ |= EOF_; /* Its end of file */
cmp_size = encode (data_read, /* Encode size */
&buff->blk_dat, /* Source */
comp_buffer); /* Destination */
if ( cmp_size < data_read ) /* If compressed */
{
buff->len = (cmp_size+OVRHD); /* Length of block */
buff->blk_typ |= COMP; /* Show compressed */
memcpy (&buff->blk_dat, /* Start of data */
comp_buffer, /* Copy from here */
cmp_size); /* This much */
}
calc_crc(SET_CRC, /* Calculate CRC */
buff->len , /* Length of block */
out_buffer); /* Where data is */
status = send_blk( /* Send the block */
buff->len, /* Block length */
&syst, /* Read block ptr. */
out_buffer); /* Buffer pointer */
time(&finish); /* Get end time */
if (finish - start) /* Check div/0 */
{
#ifdef FTIME
dat_tmp = (double) data_read;
syst.s_cps = (short) (dat_tmp /
difftime(finish,start));
#else
syst.s_cps = (short) /* Calc Block CPS */
(data_read / (finish - start) );
#endif
}
if ( buff->blk_typ == EOF_) /* Last record */
break;
}
syst.s_sta = done; /* Assume normal */
if (status)
{
cancel(); /* Send ^Xes */
syst.s_sta = abrt; /* Was aborted */
}
close_chan(port); /* Close the port */
file_io(CLOSE, &handle); /* Close the file */
screen (SCR_SYS,&syst); /* Show status block */
}
else /* File not found */
{
status = JM_FNF;
}
break; /* End of CASE 'S' */
}
}
cleanup:
free (in_buffer); /* Free buffers */
free (out_buffer);
free (comp_buffer);
/* Two-second timer to display error messages */
if (status != JM_NRM)
{
time(&finish); /* Get system clock */
finish += 2; /* Add two seconds */
start = 0;
while ( finish > start ) /* Wait until the same */
time(&start);
}
screen (SCR_END); /* Clear the screen */
return status; /* Normal exit */
}
/****************************************************************************/
/* Send the JMODEM block */
/****************************************************************************/
word send_blk (word blk_len,
register SYS *sys_ptr,
register byte *buffer)
{
byte ack_buf; /* Buffer for ACK/NAK */
word tries = 10; /* Attempts to send the block */
while ((tries--) && (!user_abort))
{
write_chan(blk_len,buffer); /* Send the JMODEM block */
flush(); /* Clear back channel noise */
do
{
ack_buf = (char) 0x00; /* Clear the return buffer */
read_chan(1,&ack_buf); /* Receive a response */
} while ( (ack_buf != ACK) /* Stay in loop until we */
&& (ack_buf != CAN) /* ... get something useful */
&& (ack_buf != NAK) /* This helps re-sync in noise */
&& (ack_buf == (char) 0x00)
&& (!user_abort) );
if ( (ack_buf == CAN)
|| user_abort ) /* Check for an abort */
break; /* User aborted */
if (ack_buf == ACK) /* If good block */
{
if (tries == 9) /* If no retries */
{
sys_ptr->s_len += 512; /* Increase block-size */
if (sys_ptr->s_len > DAT_MAX) /* If too large */
sys_ptr->s_len = DAT_MAX;
}
else
{
tries = 9 - tries; /* Use for divisor */
sys_ptr->s_len = /* Update block length */
sys_ptr->s_len / tries; /* Div block size */
if (sys_ptr->s_len < 0x40) /* If less than minimum */
sys_ptr->s_len = 0x40; /* Set to minimum */
}
sys_ptr->s_sta = okay; /* Show status is okay */
return JM_NRM; /* Show good */
}
sys_ptr->s_sta = retry; /* Show a retry */
screen (SCR_SYS, sys_ptr); /* Write to screen */
}
cancel(); /* Send cancel (^Xes) */
return JM_ABT; /* Abort local program */
}
/****************************************************************************/
/* Receive the JMODEM block */
/****************************************************************************/
word recv_blk (word *blk_len,
register byte *buffer)
{
register JBUF *buff; /* Pointer type JBUF */
byte nak_buf; /* Buffer for ACK/NAK */
word tries = 10; /* Attempts to receive the block */
word ret_val; /* Block length returned */
buff = (JBUF * )buffer; /* Assign pointer type JBUF */
while ((tries--) && (!user_abort))
{
ret_val = read_chan(2,buffer); /* Receive the block size */
if (ret_val == 2) /* If we received the length */
{
*blk_len = buff->len; /* So caller knows size */
if (*blk_len > DAT_LEN) /* If way out of line */
break; /* NAK it */
ret_val = read_chan( /* Get more data */
(*blk_len)-2 , /* Size to read */
&buff->blk_typ); /* Where to put it */
if (ret_val == (*blk_len)-2) /* If we got what we requested */
return JM_NRM;
}
if (buff->blk_typ == CAN) /* If transmitter sent ^Xes */
break; /* The other side has aborted */
read_chan (DAT_LEN,buffer); /* Make sure other end stops */
nak_buf = NAK; /* Get a NAK */
write_chan(1,&nak_buf); /* Send to remote */
flush(); /* Flush the buffer */
}
cancel(); /* Send cancel (^Xes) */
return JM_ABT; /* Abort local program */
}
/****************************************************************************/
/* Synchronize during receive */
/****************************************************************************/
word rx_sync()
{
byte ack_nak; /* Single byte buffer for ACK/NAK */
flush(); /* Clear the interrupt buffer */
while (!user_abort)
{
ack_nak = (char) 0x00; /* Clear the buffer */
read_chan(1,&ack_nak); /* Receive ACK, NAK, or SYN */
if (ack_nak == CAN) /* If a ^X */
break;
if ( ack_nak == ACK ) /* If a good response */
return JM_NRM; /* Show handshake */
if ( ack_nak == NAK ) /* If a good response */
{
ack_nak = ACK;
write_chan(1,&ack_nak); /* Send a ACK response */
return JM_NRM;
}
ack_nak = NAK;
write_chan(1,&ack_nak); /* Keep sending NAKs */
}
cancel(); /* Send cancel (^Xes) */
return JM_ABT;
}
/****************************************************************************/
/* Send ^Xes to cancel */
/****************************************************************************/
void cancel()
{
byte buffer = CAN;
short xes = 6;
user_abort=0; /* Reset flag so write_chan works */
while(xes--)
write_chan(1,&buffer);
}
/****************************************************************************/
/* Synchronize during transmit */
/****************************************************************************/
word tx_sync()
{
word ret_val;
ret_val = rx_sync(); /* Call same routine for receive */
if (!ret_val) /* If success */
{
flush(); /* Flush the input buffer */
timer = 5; /* 5 timer-ticks to wait */
while (timer); /* Wait for timer */
}
return ret_val; /* Return status */
}
/****************************************************************************/
/* Dummy _setenvp procedure to replace the large library module */
/****************************************************************************/
#ifdef NOENV /* If a compiler command */
void _setenvp(void); /* Dummy routine prototype */
void _setenvp() /* Dummy routine */
{}
#endif
/****************************************************************************/
/************************ E N D O F M O D U L E **************************/